import { list } from "radash";
import { useSnowflake } from "$/util/Snowflake";
import { QueryChain } from "./QueryChain";
import type { Database } from "db0";

interface TableLike extends Record<string, any> {
  id: string;
}

export class BaseMapper<T extends TableLike> {
  protected readonly db: Database;
  private readonly tableName: string;

  constructor(db: Database, tableName: string) {
    this.db = db;
    this.tableName = tableName;
  }

  async selectList(params?: Partial<T>): Promise<Array<T>> {
    const qw = QueryChain.from<T>(this.tableName, this.db, params);
    return qw.execQuery(this.db);
  }

  query(): QueryChain<T> {
    return new QueryChain<T>(this.tableName, this.db);
  }

  async getOne(params: Partial<T> = {}): Promise<T | null> {
    const qw = QueryChain.from<T>(this.tableName, this.db, params);
    return qw.one();
  }

  async selectById(id: string): Promise<T | null> {
    const qw = QueryChain.from<T>(this.tableName, this.db, {
      id: id
    } as Partial<T>);
    const items = await qw.list();
    return items.length > 0 ? items[0] : null;
  }

  async updateById(id: string, params: Partial<T>) {
    const query = new Array<string>();
    const values = new Array<any>();
    for (const key in params) {
      const value = params[key];
      if (typeof value === "undefined" || value === null) continue;
      query.push(`\`${key}\` = ?`);
      values.push(value);
    }
    if (query.length === 0) {
      // 没有更新的
      return;
    }
    const sql = `update ${this.tableName} set ${query.join(", ")} where id = ?`;
    console.log("update sql:\t\t" + sql);
    console.log("update values:\t" + values);
    const r = await this.db.prepare(sql).run(...values, id);
    console.log("update result:\t" + r.success);
  }

  async deleteById(id: string) {
    const sql = `delete from ${this.tableName} where id = ?`;
    console.log("delete sql:\t\t" + sql);
    console.log("delete values:\t" + id);
    const r = await this.db.prepare(sql).run(id);
    console.log("delete result:\t" + r.success);
  }

  async deleteByIds(ids: Array<string>) {
    const sql = `delete from ${this.tableName} where id in ${list(0, ids.length - 1, "?").join(
      ", "
    )}`;
    console.log("delete sql:\t\t" + sql);
    console.log("delete values:\t" + ids.join(", "));
    const r = await this.db.prepare(sql).run(...ids);
    console.log("delete result:\t" + r.success);
  }

  async insert(params: Partial<Omit<T, "id">>): Promise<T> {
    const query = new Array<string>();
    const values = new Array<any>();
    for (const key in params) {
      if (key === "id") continue;
      query.push(`\`${key}\``);
      values.push((params as any)[key]);
    }
    const sql = `insert into ${this.tableName} (id, ${query.join(
      ", "
    )}) values (${list(0, query.length, "?").join(", ")})`;
    console.log("insert sql:\t\t" + sql);
    console.log("insert values:\t" + values);
    const id = useSnowflake().nextId();
    const r = await this.db.prepare(sql).run(id, ...values);
    console.log("insert result:\t" + r.success);
    return {
      ...params,
      id
    } as T;
  }

  async insertBatch(params: Array<Partial<Omit<T, "id">>>): Promise<Array<string>> {
    if (params.length === 0) {
      return [];
    }

    // 获取第一个对象的键名（排除id）
    const keys = Object.keys(params[0]).filter((key) => key !== "id");
    const columnNames = keys.map((key) => `\`${key}\``);

    // 生成所有ID
    const ids: Array<string> = [];
    const allValues: Array<any> = [];

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    for (const _param of params) {
      const id = useSnowflake().nextId();
      ids.push(id);

      // 添加ID到值数组的开头
      allValues.push(id);
      // 按键顺序添加其他值
      for (const key of keys) {
        allValues.push((params as any)[key]);
      }
    }

    // 构建SQL语句
    const valuePlaceholders = params
      .map(() => `(${list(0, keys.length, "?").join(", ")})`)
      .join(", ");

    const sql = `insert into ${this.tableName} (id, ${columnNames.join(
      ", "
    )}) values ${valuePlaceholders}`;

    console.log("insertBatch sql:\t" + sql);
    console.log("insertBatch values:\t" + allValues);

    const r = await this.db.prepare(sql).run(...allValues);
    console.log("insertBatch result:\t" + r.success);

    return ids;
  }

  async insertSelf(params: Partial<T> & TableLike) {
    const query = new Array<string>();
    const values = new Array<any>();
    for (const key in params) {
      query.push(`\`${key}\``);
      values.push(params[key]);
    }
    const sql = `insert into ${this.tableName} (${query.join(
      ", "
    )}) values (${list(0, query.length - 1, "?").join(", ")})`;
    console.log("insert sql:\t\t" + sql);
    console.log("insert values:\t" + values);
    const r = await this.db.prepare(sql).run(...values);
    console.log("insert result:\t" + r.success);
  }
}
